/* Filename: mydaemon.c
 * Description:
 * $ ./mydaemon /tmp/out
 * $ ps axf
 * $ cat /var/log/syslog
 * $ kill -9 `pidof mydaemon`
 * Last modified: humble 20200404 23:11
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <signal.h>

static FILE *fp = NULL;

static void usage(void);

static int daemonize(void)
{
    pid_t pid;
    int fd;

    pid = fork();
    if(pid < 0){
        syslog(LOG_ERR, "%m");
        return -1;
    }
    else if(pid > 0){
        exit(0); //why parent need to exit?
    }

    fd = open("/dev/null", O_RDWR);
    if(fd < 0){
        syslog(LOG_ERR, "%m");
        return -1;
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);

    if(fd > 2){
        close(fd);
    }

    setsid();

    chdir("/"); //避免当前进程占用的目录被卸载不掉(busy)，切换到根目录(一般根不会被卸载)
    //umask(0); //如果文件不需要再新增文件就umask(0)
    return 0;
}

void daemon_exit(int s)
{
    if(s == SIGINT){ }
    else if(s == SIGQUIT){ }
    else if(s == SIGTERM){ }

    fclose(fp);
    closelog();
    exit(0);
}

int main(int argc, char **argv)
{
    int i = 1;
    struct sigaction sa;

    if(argc != 2){
        fprintf(stderr, "argc !=2");
        usage();
        exit(1);
    }

    /* 多个信号共用一个处理函数，会有函数重入问题
    signal(SIGINT, daemon_exit);
    signal(SIGQUIT, daemon_exit);
    signal(SIGTERM, daemon_exit);
    */

    //sigaction 多个信号共用一个函数避免函数重入
    sa.sa_handler = daemon_exit;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGINT);
    sigaddset(&sa.sa_mask, SIGQUIT);
    sigaddset(&sa.sa_mask, SIGTERM);
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    openlog("mydaemon", LOG_PID, LOG_DAEMON);

    if(0 > daemonize()){
        //perror("daemonize()");
        syslog(LOG_ERR, "daemonize() failed");
        exit(1);
    }
    syslog(LOG_INFO, "daemonize() succeed");

    fp = fopen(argv[1], "w");
    if(!fp){
        //perror("fopen()"); //已脱离控制终端，无法打印，所以需要用到系统日志
        syslog(LOG_ERR, "fopen() %m");
        exit(1);
    }
    syslog(LOG_INFO, "%s is opened", argv[1]);

    while(i){
        syslog(LOG_DEBUG, "%d is printed", i);
        fprintf(fp, "%d\n", i++);
        fflush(fp);
        sleep(1);
    }

    return 0;
}

static void usage(void)
{
#define USAGE "Usage:\n\
  ./mydaemon <filename>\n"
    printf(USAGE);
}
